unit Globals;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, FtpSrvC, Unit1;

  Type
  TACEHeader = Record
    ACEType  : Byte;
    ACEFlags : Byte;
    ACESize  : Word;
  End;

  TAccessAllowedACE = Record
    Header   : TACEHeader;
    Mask     : Integer;
    SIDStart : Integer;
  End;

  TAccessDeniedACE = Record
    Header   : TACEHeader;
    Mask     : Integer;
    SIDStart : Integer;
  End;

  Type ACLRecord=Record
     InUse:Boolean;
     Client:TFtpCtrlSocket;
     UserToken:THandle;
  End;

Const
     Access_Allowed_ACE_Type  = 0;
     Access_Denied_ACE_Type   = 1;
     MAXACL                   = 10000;

Var
   ACL:Array[1..MAXACL] Of ACLRecord;
   AutoStart:Boolean;
   ReadOnly:Boolean;
   MaxConnections:Integer;
   FTPPort:Integer;
   FTPBanner:String;
   LogonHome:Boolean;
   HomeDir:String;

function  GetAccessRights(AM:Integer):String;
procedure LoadMemberships(UserName:String;MemberList:TStringList);
procedure LoadAccessRights(TheFilePath:String;Users:TStringList;ACES:TStringList);
function  FileAccess(TheFile:String;UserName:String;ACE:String):Boolean;

implementation

Function GetAccessRights(AM:Integer):String;
Begin
  If ((AM And Standard_Rights_Read)<>0) Then Result:='R' Else Result:='-';
  If ((AM And Standard_Rights_Write)<>0) Then Result:=Result+'W' Else Result:=Result+'-';
  If ((AM And Standard_Rights_Execute)<>0) Then Result:=Result+'X' Else Result:=Result+'-';
  If ((AM And _Delete)<>0) Then Result:=Result+'D' Else Result:=Result+'-';
  If ((AM And Write_DAC)<>0) Then Result:=Result+'P' Else Result:=Result+'-';
  If ((AM And Write_Owner)<>0) Then Result:=Result+'O' Else Result:=Result+'-';
End;

procedure LoadMemberships(UserName:String;MemberList:TStringList);
var Tmp:TStringList;
    I:Integer;
begin
     Tmp:=TStringList.Create;
     Main.NetUser1.UserName:=UserName;
     Main.NetUser1.GetLocalGroups(Tmp);
     For I:=0 To Tmp.Count-1 Do
     Begin
          MemberList.Add(UpperCase(Tmp[I]));
     End;
     Tmp.Free;
     Tmp:=TStringList.Create;
     Main.NetUser1.UserName:=UserName;
     Main.NetUser1.GetGlobalGroups(Tmp);
     For I:=0 To Tmp.Count-1 Do
     Begin
          MemberList.Add(UpperCase(Tmp[I]));
     End;
     Tmp.Free;
end;

procedure LoadAccessRights(TheFilePath:String;Users:TStringList;ACES:TStringList);
var SD:Array[0..200] of Integer;
    J:Integer;
    B,B2,B3:Bool;
    ACL:PACL;
    ACE:Pointer;
    SID:Pointer;
    AM:Integer; { access mask }
    Name,Dom:Array[0..100] of Char;
    I,K:DWORD;
    L:SID_NAME_USE;
begin
     B:=GetFileSecurity(PChar(String(TheFilePath)),DACL_Security_Information,@SD,SizeOf(SD),I);
     If (Not B) Then Exit;
     B:=GetSecurityDescriptorDACL(@SD,B2,ACL,B3);
     If (Not B) Then Exit;
     If (ACL<>nil) Then
     Begin
          For J:=0 To ACL.ACECount-1 Do
          Begin
               B:=GetACE(ACL^,J,ACE);
               If (Not B) Then Exit;
               If (TACEHeader(ACE^).ACEType=Access_Allowed_ACE_Type) Then
               Begin
                    SID:=@TAccessAllowedACE(ACE^).SIDStart;
                    AM:=TAccessAllowedACE(ACE^).Mask;
                    B2:=True;
               End Else
               Begin
                    SID:=@TAccessDeniedACE(ACE^).SIDStart;
                    AM:=TAccessDeniedACE(ACE^).Mask;
                    B2:=False;
               End;
               I:=SizeOf(Name);
               K:=SizeOf(Dom);
               B:=LookupAccountSID('',SID,@Name,I,@Dom,K,L);
               If (Not B) Then Exit;
               Users.Add(UpperCase(Name));
               ACES.Add(GetAccessRights(AM));
          End;
     End;
end;

function FileAccess(TheFile:String;UserName:String;ACE:String):Boolean;
var MemberList:TStringList;
    UserList:TStringList;
    ACES:TStringList;
    I:Integer;
    Z:Integer;
begin
     Result:=False;
     MemberList:=TStringList.Create;
     UserList:=TStringList.Create;
     ACES:=TStringList.Create;
     LoadMemberships(UserName,MemberList);
     MemberList.Add('EVERYONE'); // not added by default. FTPS user is allways member
     MemberList.Add('NETWORK'); // not added by default. FTPS user is allways member
     MemberList.Add('AUTHENTICATED USERS'); // not added by default. FTPS user is allways member
     LoadAccessRights(TheFile,UserList,ACES);
     // check for user name
     For I:=0 To UserList.Count-1 Do
     Begin
          If UserList[I]=UserName Then
          Begin
               If Pos(ACE,ACES[I])>0 Then
               Begin
                    Result:=True;
                    Break;
               End;
          End;
     End;
     // now check for groups this user belongs to
     For I:=0 To UserList.Count-1 Do
     Begin
          For Z:=0 To MemberList.Count-1 Do
          Begin
               If UserList[I]=MemberList[Z] Then
               Begin
                    If Pos(ACE,ACES[I])>0 Then
                    Begin
                         Result:=True;
                         Break;
                    End;
               End;
          End;
     End;
     MemberList.Free;
     UserList.Free;
     ACES.Free;
end;

end.
